// $Id: array.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_IMPL_ARRAY_HPP
#define XPCOM_IMPL_ARRAY_HPP

#include <xpcom/config.hpp>
#include <boost/noncopyable.hpp>
#include <xpcom/array_fwd.hpp>
#include <xpcom/assert.hpp>
#include <xpcom/detail/array_access.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {
namespace impl {

using xpcom::detail::array_access;
using xpcom::detail::array_access_impl;

//
// XPCOM implementation argument translators.
// Note: The out/inout arguments must not be null.
//
// array_in(T*, size_type size, size_type length) --> const array<T>&
// array_in2(T*, size_type size) --> const array<T>&
// array_out(T**, size_type*, size_type*) --> array<T>&
// array_out2(T**, size_type*) --> array<T>&
// array_inout(T**, size_type*, size_type*) --> array<T>&
// array_inout2(T**, size_type*) --> array<T>&
//
// Fix for some interface like nsIBinaryInputStream which uses input [size_is]
// param for output array, which is supported through array_out_isize2, call
// it on array parameter.
//
// FIXME other similar condition is not handled, report it you have weird
// interface like this, ...
//

template <typename T, typename Traits = array_traits<T> >
class array_in : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_in(element_type* data, size_type size, size_type length) :
        a_(data, size, length)
    {
    }

    array_in(const element_type* data, size_type size, size_type length) :
        a_(const_cast<element_type*>(data), size, length)
    {
    }

    XPCOM_FORCEINLINE
    operator const array_type&() const
    {
        return *reinterpret_cast<const array_type*>(&a_);
    }

private:
    typename impl_type::data_type a_;
};

template <typename T, typename Traits = array_traits<T> >
class array_in2 : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_in2(element_type* data, size_type size) :
        a_(data, size)
    {
    }

    array_in2(const element_type* data, size_type size) :
        a_(const_cast<element_type*>(data), size)
    {
    }

    XPCOM_FORCEINLINE
    operator const array_type&() const
    {
        return *reinterpret_cast<const array_type*>(&a_);
    }

private:
    typename impl_type::data_type a_;
};

template <typename T, typename Traits = array_traits<T> >
class array_out : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_out(element_type** data, size_type* size, size_type* length) :
        data_(data),
        size_(size),
        length_(length),
        a_()
    {
    }

    ~array_out()
    {
        a_.release(data_, size_, length_);
    }

    XPCOM_FORCEINLINE
    operator array_type&()
    {
        return *reinterpret_cast<array_type*>(&a_);
    }

    XPCOM_FORCEINLINE
    void operator=(array_type retval)
    {
        static_cast<array_type&>(*this).swap(retval);
    }

private:
    element_type** data_;
    size_type* size_;
    size_type* length_;
    impl_type a_;
};

template <typename T, typename Traits = array_traits<T> >
class array_out2 : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_out2(element_type** data, size_type* size) :
        data_(data),
        size_(size),
        a_()
    {
    }

    ~array_out2()
    {
        a_.release(data_, size_);
    }

    XPCOM_FORCEINLINE
    operator array_type&()
    {
        return *reinterpret_cast<array_type*>(&a_);
    }

    XPCOM_FORCEINLINE
    void operator=(array_type retval)
    {
        static_cast<array_type&>(*this).swap(retval);
    }

private:
    element_type** data_;
    size_type* size_;
    impl_type a_;
};

template <typename T, typename Traits = array_traits<T> >
class array_inout : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_inout(element_type** data, size_type* size, size_type* length) :
        data_(data),
        size_(size),
        length_(length),
        a_(*data, *size, *length)
    {
        // XXX This could be omitted?
        *data_ = 0;
        *size_ = 0;
        *length_ = 0;
    }

    ~array_inout()
    {
        a_.release(data_, size_, length_);
    }

    XPCOM_FORCEINLINE
    operator array_type&()
    {
        return *reinterpret_cast<array_type*>(&a_);
    }

private:
    element_type** data_;
    size_type* size_;
    size_type* length_;
    impl_type a_;
};

template <typename T, typename Traits = array_traits<T> >
class array_inout2 : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_inout2(element_type** data, size_type* size) :
        data_(data),
        size_(size),
        a_(*data, *size)
    {
        // XXX This could be omitted?
        *data_ = 0;
        *size_ = 0;
    }

    ~array_inout2()
    {
        a_.release(data_, size_);
    }

    XPCOM_FORCEINLINE
    operator array_type&()
    {
        return *reinterpret_cast<array_type*>(&a_);
    }

private:
    element_type** data_;
    size_type* size_;
    impl_type a_;
};

template <typename T, typename Traits = array_traits<T> >
class array_out_isize2 : boost::noncopyable
{
    typedef array<T, Traits> array_type;
    typedef typename array_type::element_type element_type;
    typedef typename array_type::size_type size_type;
    typedef typename array_access<array_type>::impl_type impl_type;
public:
    array_out_isize2(element_type** data, size_type size) :
        data_(data),
        size_(size),
        a_()
    {
        *data_ = 0;
    }

    ~array_out_isize2()
    {
        // Check for returned array's size, if not equal to requested one
        // then there must be something wrong happened, so discard it.
        // XXX This behavior will be checked later
        XPCOM_ASSERT(a_.length() == size_);
        if (a_.length() == size_)
            a_.release(data_, &size_);
    }

    XPCOM_FORCEINLINE
    operator array_type&()
    {
        return *reinterpret_cast<array_type*>(&a_);
    }

    XPCOM_FORCEINLINE
    void operator=(array_type retval)
    {
        static_cast<array_type&>(*this).swap(retval);
    }

private:
    element_type** data_;
    size_type size_;
    impl_type a_;
};

} // namespace xpcom::impl
} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_IMPL_ARRAY_HPP
